C语言中的指针

880次阅读
没有评论

共计 2633 个字符,预计需要花费 7 分钟才能阅读完成。

提醒:本文最后更新于 2023-10-07 17:34,文中所关联的信息可能已发生改变,请知悉!

什么是指针?

指针是一种变量,它存储了另一个变量的内存地址。我们可以通过指针来访问或修改它所指向的变量的值。指针的类型决定了它所指向的变量的类型,以及它可以进行的操作。

例如,下面的代码定义了一个整型变量 a,一个整型指针p,并将p 初始化为 a 的地址:

int a = 10; // 定义一个整型变量 a,并赋值为 10
int *p = &a; // 定义一个整型指针 p,并将其初始化为 a 的地址

在这里,我们使用了 & 运算符来获取变量的地址,也就是它在内存中的位置。我们也可以使用 * 运算符来获取指针所指向的变量的值,这称为 解引用 指针。例如:

printf("The value of a is %d\n", a); // 输出 a 的值
printf("The address of a is %p\n", &a); // 输出 a 的地址
printf("The value of p is %p\n", p); // 输出 p 的值,即 a 的地址
printf("The value of *p is %d\n", *p); // 输出 * p 的值,即 a 的值

输出结果可能是:

The value of a is 10
The address of a is 0x7ffeedb8f9ec
The value of p is 0x7ffeedb8f9ec
The value of *p is 10

从输出结果可以看出,p&a 是相同的,都是 a 的地址;而 *pa是相同的,都是10

指针有什么用?

指针有很多用途,其中最常见的有以下几个:

  • 动态内存分配 :我们可以使用指针来分配和释放内存空间,从而实现动态地创建和销毁数据结构。例如,我们可以使用函数malloc() 来分配一块内存,并返回一个指向该内存的指针;我们也可以使用函数 free() 来释放一个指向已分配内存的指针。例如:
int *arr = malloc(10 * sizeof(int)); // 分配一个大小为 10 个整数的数组,并返回一个指向该数组首元素的指针
if (arr == NULL) {printf("Memory allocation failed\n"); // 如果分配失败,则输出错误信息
    exit(1); // 并退出程序
}
for (int i = 0; i < 10; i++) {arr[i] = i + 1; // 使用下标运算符 [] 来访问和修改数组元素,等价于 *(arr + i) = i + 1;
}
for (int i = 0; i < 10; i++) {printf("%d ", arr[i]); // 输出数组元素
}
printf("\n");
free(arr); // 释放数组占用的内存空间

输出结果是:

1 2 3 4 5 6 7 8 9 10 
  • 传递参数:我们可以使用指针来传递参数给函数,从而实现对参数进行修改或节省复制参数所需的时间和空间。例如,我们可以使用指针来实现交换两个变量的值的函数:
void swap(int *x, int *y) { // 定义一个交换两个整型变量的值的函数,参数为两个整型指针
    int temp = *x; // 定义一个临时变量,并将 * x 的值赋给它
    *x = *y; // 将 * y 的值赋给 *x
    *y = temp; // 将临时变量的值赋给 *y
}

int main() {
    int a = 10, b = 20; // 定义两个整型变量 a 和 b,并赋值为 10 和 20
    printf("Before swap: a = %d, b = %d\n", a, b); // 输出交换前的值
    swap(&a, &b); // 调用交换函数,传入 a 和 b 的地址
    printf("After swap: a = %d, b = %d\n", a, b); // 输出交换后的值
    return 0;
}

输出结果是:

Before swap: a = 10, b = 20
After swap: a = 20, b = 10
  • 实现数据结构:我们可以使用指针来实现一些复杂的数据结构,如链表、树、图等。这些数据结构通常由一些节点组成,每个节点包含一些数据和指向其他节点的指针。例如,我们可以使用指针来实现一个单向链表:
struct node { // 定义一个链表节点的结构体,包含一个整型数据和一个指向下一个节点的指针
    int data;
    struct node *next;
};

struct node *head = NULL; // 定义一个指向链表头节点的指针,并初始化为 NULL

void insert(int x) { // 定义一个在链表头部插入一个节点的函数,参数为要插入的数据
    struct node *new_node = malloc(sizeof(struct node)); // 分配一个新节点的内存空间,并返回一个指向该节点的指针
    if (new_node == NULL) {printf("Memory allocation failed\n"); // 如果分配失败,则输出错误信息
        exit(1); // 并退出程序
    }
    new_node->data = x; // 将要插入的数据赋给新节点的数据域
    new_node->next = head; // 将新节点的指针域指向原来的头节点
    head = new_node; // 将头指针指向新节点
}

void print() { // 定义一个打印链表所有元素的函数
    struct node *cur = head; // 定义一个当前节点的指针,并初始化为头节点
    while (cur != NULL) { // 当当前节点不为空时,循环执行以下操作
        printf("%d ", cur->data); // 输出当前节点的数据
        cur = cur->next; // 将当前节点移动到下一个节点
    }
    printf("\n");
}

int main() {insert(10); // 在链表头部插入 10
    insert(20); // 在链表头部插入 20
    insert(30); // 在链表头部插入 30
    print(); // 打印链表所有元素
    return 0;
}

输出结果是:

30 20 10 

指针有什么注意事项?

指针虽然强大,但也有一些需要注意的地方,否则可能会导致程序出错或崩溃。以下是一些常见的注意事项:

  • 不要使用未初始化或无效的指针:如果一个指针没有被初始化或已经被释放,那么它可能会指向任意的内存地址,这可能会导致访问或修改不属于程序的内存空间,从而引发错误或崩溃。因此,在使用指针之前,我们应该确保它已经被正确地初始化或分配,并在释放后将其置为 NULL。
  • 不要越过数组或字符串的边界 :如果一个指针是用来访问数组或字符串中的元素,那么我们应该确保它不越过数组或字符串的边界,否则可能会访问到无效的内存地址,从而引发错误或崩溃。这是一个常见的编程错误,也称为 缓冲区溢出 。为了避免这种错误,我们应该在使用指针时检查它是否在合法的范围内,或者使用一些安全的函数来操作数组或字符串,如strncpy()strncat()snprintf() 等。

正文完
 0
历史的配角
版权声明:本站原创文章,由 历史的配角 于2023-09-14发表,共计2633字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
验证码

您无法复制此页面的内容

了解 未来日记 的更多信息

立即订阅以继续阅读并访问完整档案。

Continue reading